package com.ttjj.service.navigation;

import com.ttjj.entity.map.Coordinate;
import com.ttjj.entity.map.Position;
import com.ttjj.entity.navi.*;
import com.ttjj.entity.player.PlayerStateVo;
import com.ttjj.enums.EnumDirection;
import com.ttjj.utils.AStarAlgorithmUtil;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.collections.Sets;


import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class TerrainNavigation {
    private static final Logger logger = LoggerFactory.getLogger(TerrainNavigation.class);

    private static final AtomicInteger needCalTimes = new AtomicInteger(0);
    private static final AtomicBoolean isCalculating = new AtomicBoolean(false);
    private static boolean isReady = false;

    public TerrainNavigation() {
    }


    public static boolean isReady() {
        return isReady;
    }

    public static boolean isCalculating() {
        return isCalculating.get();
    }

    public static void reset(boolean block, boolean hasShovel) {
        isReady = false;
        TerrainNaviMap.reset();
        if (needCalTimes.get() < 2) {
            needCalTimes.incrementAndGet();
            startComputeAllTerrainRoute(block, hasShovel);
        }

    }

    public static void terrainChange(List<Position> posSet, boolean block) {
        TerrainNaviMap.terrainChange(posSet);
        if (needCalTimes.get() < 2) {
            needCalTimes.incrementAndGet();
            startComputeAllTerrainRoute(block, false);
        }

    }

    public static double getTerrainDistance(Coordinate beginCod, Coordinate endCod, int speed) {
//        DistanceResult distanceRet = new DistanceResult(beginCod, endCod);
//        distanceRet.compute(speed);
//        return distanceRet.getStepCost();

        int[] startGrid = AStarAlgorithmUtil.convertToGridCoordinate(beginCod.getY(), beginCod.getX());
        int[] endGrid = AStarAlgorithmUtil.convertToGridCoordinate(endCod.getY(), endCod.getX());
        char[][] grid = AStarAlgorithmUtil.listToCharArray(TerrainAnalyzer.getMap());
        Set<int[]> users = Sets.newHashSet();
        for(PlayerStateVo farmerStateVo: new ArrayList<>(TerrainAnalyzer.OTHER_PLAYER)){
            int stX = farmerStateVo.getY();
            int stY = farmerStateVo.getX();
            int[] stGrid = AStarAlgorithmUtil.convertToGridCoordinate(stX, stY);
            users.add(stGrid);
        }

        List<int[]> res = AStarAlgorithmUtil.aStarSearchPoint(grid, startGrid[0], startGrid[1], endGrid[0], endGrid[1], users);

        return res != null ? res.size(): 100;
    }

    public static Position getNextPos(Position beginPos, Position endPos) {
        if (!beginPos.equals(endPos)) {
            TerrainNaviRoute route = TerrainNaviMap.getEffectiveRoute(beginPos, endPos);
            if (route == null) {
                logger.error("未获取到{} 到 {} 路径,获取到下一步坐标", beginPos, endPos);
            }
            List<Position> passBys = route.getPassBys(beginPos);
            if (!passBys.isEmpty()) {
                return passBys.get(1);
            }
        }
        return beginPos;
    }

    /**
     * 获取最秀下步路
     *
     * @param playerStateVo
     * @param beginCod
     * @param endCod
     * @return
     */
    public static Coordinate getNextCod(PlayerStateVo playerStateVo, Coordinate beginCod, Coordinate endCod) {
        EnumDirection direct = getNextDirect(beginCod, endCod).getNextDirection();
        int speed = TerrainAnalyzer.getSpeedByCod(playerStateVo, beginCod);
        return beginCod.stepByDirect(direct, speed);
    }

    public static NextDirectionInfo getNextDirect(Coordinate beginCod, Coordinate endCod) {
        NextDirectionInfo resultInfo = new NextDirectionInfo();
        EnumDirection result = EnumDirection.STOP;
        Position beginPos = beginCod.toPosition();
        Position endPos = endCod.toPosition();
        if (beginPos.equals(endPos)) {
            EnumDirection verticalStepD = endCod.getVerticalDirectFrom(beginCod);
            EnumDirection horizontalStepD = endCod.getHorizontalDirectFrom(beginCod);
            result = verticalStepD == EnumDirection.STOP ? horizontalStepD : verticalStepD;
        } else {
            TerrainNaviRoute route = TerrainNaviMap.getEffectiveRoute(beginPos, endPos);
            List<Position> passBys = new ArrayList<>();
            if (route != null) {
                passBys = route.getPassBys(beginPos);
            } else {
                logger.error("未获取到{} 到 {} 的路径", beginPos, endPos);
            }
            if (!passBys.isEmpty()) {
                Position nextStepPos = passBys.get(1);
                Coordinate beginPosCod = beginPos.toCoordinate();
                // 像素移动
                EnumDirection verticalPosD = beginPosCod.getVerticalDirectFrom(beginCod);
                EnumDirection horizontalPosD = beginPosCod.getHorizontalDirectFrom(beginCod);
                // 区块移动
                EnumDirection verticalStepD = nextStepPos.getVerticalDirectFrom(beginPos);
                EnumDirection horizontalStepD = nextStepPos.getHorizontalDirectFrom(beginPos);
                if (verticalPosD != EnumDirection.STOP && horizontalPosD != EnumDirection.STOP) {
                    if (verticalStepD == EnumDirection.STOP) {
                        result = verticalPosD;
                    } else {
                        result = horizontalPosD;
                    }
                } else if (verticalPosD == EnumDirection.STOP && horizontalPosD == EnumDirection.STOP) {
                    result = horizontalStepD == EnumDirection.STOP ? verticalStepD : horizontalStepD;
                } else if (verticalPosD == EnumDirection.STOP) {
                    result = horizontalStepD == EnumDirection.STOP ? horizontalPosD : horizontalStepD;
                } else {
                    result = verticalStepD == EnumDirection.STOP ? verticalPosD : verticalStepD;
                }
            }
        }
        resultInfo.setNextDirection(result);
        resultInfo.setDest(endCod);
        return resultInfo;
    }

    /**
     * 计算路径图
     *
     * @param block
     */
    private static void startComputeAllTerrainRoute(boolean block, boolean hasShovel) {
        if (needCalTimes.get() > 0 && !isCalculating.get() && isCalculating.compareAndSet(false, true)) {
            Future<?> submit = Executors.newSingleThreadExecutor().submit(() -> {
                while (needCalTimes.get() > 0) {
//                    logger.debug("地形导航计算开始");
                    long begin = System.currentTimeMillis();

                    boolean var19 = false;

                    long end;
                    label203:
                    {
                        try {
                            var19 = true;

                            for (int i = 0; i < 16; ++i) {
                                for (int j = 0; j < 28; ++j) {
                                    Position beginPos = new Position(i, j);
                                    if (!TerrainAnalyzer.isOverlap(beginPos, hasShovel)) {
                                        HashSet<TerrainStep> openSet = new HashSet<>();
                                        HashSet<TerrainStep> closeSet = new HashSet<>();
                                        TerrainStep beginStep = new TerrainStep(beginPos, null);
                                        openSet.add(beginStep);

                                        label185:
                                        while (!openSet.isEmpty()) {
                                            TerrainStep curStep = openSet.stream().findFirst().get();
                                            openSet.remove(curStep);
                                            closeSet.add(curStep);
                                            Iterator<TerrainStep> nextStepIterator = curStep.getNextSteps().iterator();

                                            while (true) {
                                                while (true) {
                                                    TerrainStep nextStep;
                                                    TerrainNaviRoute route;
                                                    do {
                                                        if (!nextStepIterator.hasNext()) {
                                                            continue label185;
                                                        }

                                                        nextStep = nextStepIterator.next();
                                                        route = TerrainNaviMap.getRoute(beginPos, nextStep.getPos());
//                                                        System.out.println(beginPos+" "+nextStep.getPos() +" "+ route);
                                                    } while (!route.isReachable());

                                                    boolean inOpenSet = openSet.contains(nextStep);
                                                    boolean inCloseSet = closeSet.contains(nextStep);
                                                    if (!inOpenSet && !inCloseSet) {
                                                        if (nextStep.getTotalCost() < route.getTotalCost()) {
                                                            route.setRoute(nextStep);
                                                            openSet.add(nextStep);
                                                        } else {
                                                            openSet.add(route.getAnotherFinalStep(nextStep.getPos()));
                                                        }
                                                    } else if (nextStep.getTotalCost() < route.getTotalCost()) {
                                                        route.setRoute(nextStep);
                                                        if (inOpenSet) {
                                                            openSet.remove(nextStep);
                                                        } else {
                                                            closeSet.remove(nextStep);
                                                        }

                                                        openSet.add(nextStep);
                                                    }
                                                }
                                            }
                                        }
                                    } else {
                                        if (logger.isDebugEnabled()) {
//                                            logger.info("坐标【{},{}】类型 {} 无法行走，故不建立路径图", beginPos.getX(), beginPos.getY(),
//                                                    TerrainAnalyzer.getMap().get(beginPos.getX()).get(beginPos.getY()));
                                        }
                                    }
                                }
                            }

                            var19 = false;
                            break label203;
                        } catch (Exception var20) {
                            logger.error("全地形导航算法异常", var20);
                            var19 = false;
                        } finally {
                            if (var19) {
                                long endx = System.currentTimeMillis();
                                logger.error("全地形导航计算耗时1：{} ms", endx - begin);
                                needCalTimes.decrementAndGet();
                                if (!isReady) {
                                    isReady = true;
                                }

                            }
                        }

                        end = System.currentTimeMillis();
                        logger.info("全地形导航计算耗时2：{} ms", end - begin);
                        needCalTimes.decrementAndGet();
                        if (!isReady) {
                            isReady = true;
                        }
                        continue;
                    }

                    end = System.currentTimeMillis();
                    logger.info("全地形导航计算耗时3：{} ms", end - begin);
                    needCalTimes.decrementAndGet();
                    if (!isReady) {
                        TerrainNaviMap.setOkRoute();
                        isReady = true;
                    }
                }

                isCalculating.set(false);
                startComputeAllTerrainRoute(block, hasShovel);
            });
            if (block) {
                try {
                    submit.get();
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }
}
